/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package xenon3d.scene;

import javax.media.opengl.GL2;

/**
 * Appearance defines all rendering state that can be set as a component object
 * of a Shape node.
 * 
 * @author Volker Everts
 * @version 0.1 - 04.09.08: Created
 */
public class Appearance extends NodeComponent {

    // <editor-fold defaultstate="collapsed" desc=" Static Attributes ">
    
    /** The default appearance. */
    static final Appearance DEFAULT = new Appearance();
    
    /** The last active appearance. */
    static Appearance last = DEFAULT;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Private Fields ">
    
    /** The point attribtes object for this appearance. */
    private PointAttributes pa = PointAttributes.DEFAULT;
    
    /** The line attribtes object for this appearance. */
    private LineAttributes la = LineAttributes.DEFAULT;
    
    /** The polygon attributes object for this appearance. */
    private PolygonAttributes ga = PolygonAttributes.DEFAULT;
    
    /** The rendering attributes object for this appearance. */
    private RenderingAttributes ra = RenderingAttributes.DEFAULT;
    
    /** The coloring attributes object for this appearance. */
    private ColoringAttributes ca = ColoringAttributes.DEFAULT;
    
    /** The material object for this appearance. */
    private Material ma = Material.DEFAULT;
    
    /** The texture object for this appearance. */
    private Texture2D ta = Texture2D.DEFAULT;
    
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc=" Initialization ">
    
    /**
     * Creates a new Appearance.
     */
    public Appearance() {}
    
    /**
     * Creates a new Appearance with the specified Attribute.
     * @param attr the appearance attribute
     */
    public Appearance(Attributes attr) {
        if (attr instanceof PointAttributes) pa = (PointAttributes) attr;
        else if (attr instanceof LineAttributes) la = (LineAttributes) attr;
        else if (attr instanceof PolygonAttributes) ga = (PolygonAttributes) attr;
        else if (attr instanceof RenderingAttributes) ra = (RenderingAttributes) attr;
        else if (attr instanceof ColoringAttributes) ca = (ColoringAttributes) attr;
        else if (attr instanceof Material) ma = (Material) attr;
        else if (attr instanceof Texture2D) ta = (Texture2D) attr;
        else throw new IllegalArgumentException();  // should never happen
    }
    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Properties ">

    /**
     * Sets the PointAttributes object to the specified object. Setting it to
     * null will result in default attribute usage.
     * @param attr the PointAttributes to set
     */
    public void setPointAttributes(PointAttributes attr) {
        checkCapability(Capability.AllowPointAttributesWrite);
        if (attr == null) attr = PointAttributes.DEFAULT;
        if (pa == attr) return;
        pa.removeNotify(this);
        attr.addNotify(this);
        pa = attr;
    }

    /**
     * Retrieves the current PointAttributes object.
     * @return the PointAttributes object, or null, if no point attributes
     * have been set
     */
    public PointAttributes getPointAttributes() {
        if (pa == PointAttributes.DEFAULT) return null;
        return pa;
    }
    
    /**
     * Sets the LineAttributes object to the specified object. Setting it to
     * null will result in default attribute usage.
     * @param attr the LineAttributes to set
     */
    public void setLineAttributes(LineAttributes attr) {
        checkCapability(Capability.AllowLineAttributesWrite);
        if (attr == null) attr = LineAttributes.DEFAULT;
        if (la == attr) return;
        la.removeNotify(this);
        attr.addNotify(this);
        la = attr;
    }

    /**
     * Retrieves the current LineAttributes object.
     * @return the LineAttributes object, or null, if no line attributes
     * have been set
     */
    public LineAttributes getLineAttributes() {
        if (la == LineAttributes.DEFAULT) return null;
        return la;
    }
    
    /**
     * Sets the PolygonAttributes object to the specified object. Setting it to
     * null will result in default attribute usage.
     * @param attr the PolygonAttributes to set
     */
    public void setPolygonAttributes(PolygonAttributes attr) {
        checkCapability(Capability.AllowPolygonAttributesWrite);
        if (attr == null) attr = PolygonAttributes.DEFAULT;
        if (ga == attr) return;
        ga.removeNotify(this);
        attr.addNotify(this);
        ga = attr;
    }

    /**
     * Retrieves the current PolygonAttributes object.
     * @return the PolygonAttributes object, or null, if no polygon attributes
     * have been set
     */
    public PolygonAttributes getPolygonAttributes() {
        if (ga == PolygonAttributes.DEFAULT) return null;
        return ga;
    }
    
    /**
     * Sets the RenderingAttributes object to the specified object. Setting it
     * to null will result in default attribute usage.
     * @param attr the RenderingAttributes to set
     */
    public void setRenderingAttributes(RenderingAttributes attr) {
        checkCapability(Capability.AllowRenderingAttributesWrite);
        if (attr == null) attr = RenderingAttributes.DEFAULT;
        if (ra == attr) return;
        ra.removeNotify(this);
        attr.addNotify(this);
        ra = attr;
    }
    
    /**
     * Retrieves the current RenderingAttributes object.
     * @return the RenderingAttributes object, or null, if no rendering
     * attributes have been set
     */
    public RenderingAttributes getRenderingAttributes() {
        if (ra == RenderingAttributes.DEFAULT) return null;
        return ra;
    }
    
    /**
     * Sets the ColoringAttributes object to the specified object. Setting it to
     * null will result in default attribute usage.
     * @param attr the ColoringAttributes object
     */
    public void setColoringAttributes(ColoringAttributes attr) {
        checkCapability(Capability.AllowColoringAttributesWrite);
        if (attr == null) attr = ColoringAttributes.DEFAULT;
        if (ca == attr) return;
        ca.removeNotify(this);
        attr.addNotify(this);
        ca = attr;
    }

    /**
     * Retrieves the current ColoringAttributes object.
     * @return the ColoringAttributes object, or null, if no coloring attributes
     * have been set
     */
    public ColoringAttributes getColoringAttributes() {
        if (ca == ColoringAttributes.DEFAULT) return null;
        return ca;
    }
    
    /**
     * Sets the material to the specified object. Setting it to null will result
     * in default attribute usage.
     * @param attr the material attribute
     */
    public void setMaterial(Material attr) {
        checkCapability(Capability.AllowMaterialWrite);
        if (attr == null) attr = Material.DEFAULT;
        if (ma == attr) return;
        ma.removeNotify(this);
        attr.addNotify(this);
        ma = attr;
    }

    /**
     * Retrieves the current Material.
     * @return the material, or null, if no material has been set
     */
    public Material getMaterial() {
        if (ma == Material.DEFAULT) return null;
        return ma;
    }
    
    /**
     * Sets the primary texture to the specified object. Setting it to null will
     * result in default attribute usage.
     * @param attr the primary texture
     */
    public void setTexture(Texture2D attr) {
        checkCapability(Capability.AllowTextureDataWrite);
        if (attr == null) attr = Texture2D.DEFAULT;
        if (ta == attr) return;
        ta.removeNotify(this);
        attr.addNotify(this);
        ta = attr;
    }

    /**
     * Retrieves the current primary texture.
     * @return the texture, or null, if no primary texture has been set
     */
    public Texture2D getTexture() {
        if (ta == Texture2D.DEFAULT) return null;
        return ta;
    }
    
    /**
     * Checks whether texture coordinates must be flipped.
     * @return true, if texture coordinates must be flipped
     */
    public boolean mustFlipTextureCoordsVertically() {
        return ta.mustFlipVertically();
    }
    
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Package Private Methods (OpenGL) ">

    /**
     * Applies this appearance to the current render state if and only if the
     * appearance has changed since the last call to this method.
     * @param gl the OpenGL gl interface object
     */
    protected void apply(GL2 gl) {
        if (this == last) return;
        if (pa != last.pa) pa.apply(gl);
        if (la != last.la) la.apply(gl);
        if (ga != last.ga) ga.apply(gl);
        if (ra != last.ra) ra.apply(gl);
        if (ca != last.ca) ca.apply(gl);
        if (ma != last.ma) ma.apply(gl);
        if (ta != last.ta) ta.apply(gl);
        last = this;
    }
    
    // </editor-fold>

} // end class Appearance